package edu.vt.ece.rt.benchmark.intruder;

import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Random;
import java.util.Vector;

import edu.vt.ece.rt.benchmark.BenchmarkThread;
import edu.vt.ece.rt.benchmark.hashmap.Hashmap;
import edu.vt.ece.rt.dstm.Context;
import edu.vt.ece.rt.dstm.ContextDelegator;
import edu.vt.ece.rt.dstm.QObject;
import edu.vt.ece.rt.dstm.TMProxy;
import edu.vt.ece.rt.quorum.ClustersManager;
import edu.vt.ece.rt.quorum.Resource;
import edu.vt.ece.rt.quorum.util.AbortException;
import edu.vt.ece.rt.util.Logger.Logger;


public class Stream extends QObject{

    long percentAttack;
    Random randomPtr;
    Vector<Packet> allocVectorPtr;
    public Queue<Packet> packetQueuePtr;
    Map<Integer, String> attackMapPtr;
    String id;
    
    public Stream (String ID, int percent) {
    	randomPtr = new Random();
    	allocVectorPtr = new Vector<Packet>();
    	packetQueuePtr = new LinkedList<Packet>();
    	attackMapPtr = new HashMap<Integer, String>();
    	percentAttack = percent;
    	id = ID;
		Resource.putQObject(getId(), this);
    }

    public Stream () {
    	randomPtr = new Random();
    	allocVectorPtr = new Vector<Packet>();
    	packetQueuePtr = new LinkedList<Packet>();
    	attackMapPtr = new HashMap<Integer, String>();
    }

    static void
    splitIntoPackets (String str,
                      Integer flowId,
                      Random randomPtr,
                      Vector<Packet> allocVectorPtr,
                      Queue<Packet> packetQueuePtr)
    {
        int numByte = str.length();
//        System.out.println("numBytes "+numByte);

        int numPacket = (int)((Math.abs(randomPtr.nextLong()) % numByte)) + 1;
//        int numPacket = (int)(50 % numByte) + 1;

        //        System.out.println("numPacket "+numPacket);

        int numDataByte = numByte / numPacket;

        int p;
        for (p = 0; p < (numPacket - 1); p++) {
            boolean status;
            Packet packet_alloc = new Packet();
//            assert(bytes);
            status = allocVectorPtr.add(packet_alloc);
//            assert(status);
            Packet packetPtr = packet_alloc;
            packetPtr.flowId      = flowId;
            packetPtr.fragmentId  = p;
            packetPtr.numFragment = numPacket;
            packetPtr.length      = numDataByte;
            packetPtr.data = str.substring( (int)(p * numDataByte), (int)((p * numDataByte) + numDataByte));
//            System.out.println("start="+(int)(p * numDataByte));
//            System.out.println("end="+(int)((p * numDataByte) + numDataByte));
            status = packetQueuePtr.add(packetPtr);
            assert(status);
        }

        boolean status;
        int lastNumDataByte = numDataByte + numByte % numPacket;
        int lastIndex = (int)((p * numDataByte) + lastNumDataByte);
        if (lastIndex == numByte) {
        	lastIndex--;
        }
        int startIndex = (int) (p * numDataByte);
//        System.out.println("last start="+lastIndex);        

//        System.out.println("last="+lastIndex);        

        Packet packet_alloc = new Packet();
//        assert(bytes);
        status = allocVectorPtr.add(packet_alloc);
        assert(status);
        Packet packetPtr = packet_alloc;
        packetPtr.flowId      = flowId;
        packetPtr.fragmentId  = p;
        packetPtr.numFragment = numPacket;
        packetPtr.length      = lastNumDataByte;
        packetPtr.data = str.substring( startIndex);
        status = packetQueuePtr.add(packetPtr);
        assert(status);
    }


    static long
    stream_generate (Stream streamPtr,
                     Dictionary dictionaryPtr,
                     long numFlow,
                     long seed,
                     long maxLength)
    {
        long numAttack = 0;

        long      percentAttack  = streamPtr.percentAttack;
        Random randomPtr      = streamPtr.randomPtr;
        Vector<Packet> allocVectorPtr = streamPtr.allocVectorPtr;
        Queue<Packet>  packetQueuePtr = streamPtr.packetQueuePtr;
        Map<Integer, String>     attackMapPtr   = streamPtr.attackMapPtr;

        Detector detectorPtr = new Detector();
        packetQueuePtr.clear();

        long range = '~' - ' ' + 1;
        assert(range > 0);

        int f;
        for (f = 1; f <= numFlow; f++) {
            String str = "";
            if ((Math.abs(randomPtr.nextInt()) % 100) < percentAttack) {           	
                int s = (int)( Math.abs(randomPtr.nextInt()) % Dictionary.global_defaultSignatures.length);
                str = Dictionary.dictionary_get(dictionaryPtr, s);
                attackMapPtr.put(f, str);
//                assert(status);
                numAttack++;
            } else {
                long length = (Math.abs(randomPtr.nextInt())  % maxLength) + 1;
//                str = (String)malloc((length + 1)  sizeof(String));
                Packet p = new Packet();
                boolean status = allocVectorPtr.add(p);
//                assert(status);
                int l;
                for (l = 0; l < length; l++) {
                    str = str + (char)(Math.abs(randomPtr.nextInt()) % range);
                }
                p.data = str;
//                String str2 = (String)malloc((length + 1)  sizeof(String));
//                assert(str2);
                String str2 = new String(str);
                Error.error error = Detector.detector_process(detectorPtr, str2);
                if (error == Error.error.ERROR_SIGNATURE) {
                    attackMapPtr.put(f,str);
                    assert(status);
                    numAttack++;
                }
//                free(str2);
            }
            
            splitIntoPackets(str, f, randomPtr, allocVectorPtr, packetQueuePtr);
            
        }

        Collections.shuffle((LinkedList<Packet>)packetQueuePtr);

//        detector_free(detectorPtr);

        return numAttack;
    }


    static Packet
    stream_getPacket (Stream streamPtr)
    {
        return streamPtr.packetQueuePtr.poll();
    }


    static Packet
    stream_getPacket (String streamId, Context parentContext)
    {
    	
		try {
			Thread.sleep(500 * (ClustersManager.baseClusterId + 1));
		} catch (InterruptedException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		

		Long priority = System.currentTimeMillis();

		for (int i = 0; i < 0x7fffffff; i++) {

			((BenchmarkThread)Thread.currentThread()).collectStatsRoot(i);
			Context context = ContextDelegator.getNestedContext();
			context.setParent(parentContext);
			context.setDescription("find element in bucket");
			context.sanityNested();
			context.setPriority(priority);
			
			Logger.debug("----Started----- R Transaction "+context.getContextId()+" on "+ClustersManager.baseClusterId);
			try {
				Stream stream = (Stream) TMProxy.getTMProxy().write(streamId, context.getContextId());
				Packet packet = Stream.stream_getPacket(stream);
				
				
				if (TMProxy.getTMProxy().requestCommit(context.getContextId())) {
					context.unsetParent(parentContext);
					return packet;
				}
			} catch (AbortException e) {
				Logger.debug("Aborting in loop "+context.getContextId());
				e.touch();
			}
		}

		return null;
    }

    static  boolean
    stream_isAttack (Stream streamPtr, long flowId)
    {
        return streamPtr.attackMapPtr.get(flowId) != null;
    }


	public static void main(String[] args) {
    {
        int percentAttack = 10;
        long numFlow = 100;
        long seed = 0;
        long maxLength = 20;

        System.out.println("Starting...");

        Stream streamPtr = new Stream("stream",percentAttack);
//        assert(streamPtr);

        Dictionary dictionaryPtr = new Dictionary();
//        assert(dictionaryPtr);

        stream_generate(streamPtr, dictionaryPtr, numFlow, seed, maxLength);
        
        Packet bytes;
        while ((bytes = stream_getPacket(streamPtr)) != null) {
            Packet packetPtr = (Packet)bytes;
            long  flowId      = packetPtr.flowId;
            long  fragmentId  = packetPtr.fragmentId;
            long  numFragment = packetPtr.numFragment;
            long  length      = packetPtr.length;
            String data        = packetPtr.data;
            long l;
//            System.out.println("flow="+flowId+" "+ " frag="+ fragmentId+ " num="+numFragment);
//            System.out.println(data);
//            System.out.println("");
        }

//        stream_free(streamPtr);

        System.out.println("Done.");

//        return 0;
    }
	
	}

	@Override
	public QObject clone() {
		// TODO Auto-generated method stub
		Stream s = new Stream();
		s.id = id;
		s.randomPtr = randomPtr;
		s.allocVectorPtr = allocVectorPtr;
		s.attackMapPtr = attackMapPtr;
		s.percentAttack = percentAttack;
		synchronized (packetQueuePtr) {
			for (Packet p : packetQueuePtr) {
				s.packetQueuePtr.add(p);
				break;
			}
		}
		s.setCloneOwner(getCloneOwner());
		s.setVersion(getVersion());
		
		return s;
	}

	@Override
	public String getId() {
		// TODO Auto-generated method stub
		return id;
	}
	}
